//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <math.h>

#include <iostream>
#include <fstream>

#include "zlib.h"

#include "libraries.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LINGOLENGTH  4
#define LENGTHARRAY  40096
#define CUTOFFFREQ   1
#define ORIGINALFREQ 0
#define CHANGEDIGIT  '?'
#define EUCLIDIAND   1
#define POSITIONL    1
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
struct SmilesRef {

  String* smiles;
  String* ref;

  SmilesRef() {
    this->smiles = new String();
    this->ref    = new String();
  }

  SmilesRef(char* smiles, char* ref) {
    if (smiles && ref) {
      this->smiles = new String(smiles);
      this->ref    = new String(ref);
      delete[] smiles;
      delete[] ref;
    }
  }

  ~SmilesRef() {
    if (this->smiles) delete this->smiles;
    if (this->ref)    delete this->ref;
  }

  struct SmilesRef operator=(const struct SmilesRef &smilesRef) {
    this->smiles->In(smilesRef.smiles);
    this->ref->In(smilesRef.smiles);
    return *this;
  };

};

typedef struct SmilesRef SMILESRef;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace LingoSpace {

  bool FindReference(TNode <SMILESRef>* NodeT, void* ref = NULL) {
    return (!strcmp(NodeT->Info()->ref->Out(), (char*)ref))?true:false;
  }
  
};
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void trim(char *stream) {

  int i, j;
  for (i = 0, j = 0; i < strlen(stream); i++)
    if ((stream[i] != '\n') && (stream[i] != '\r') && (stream[i] != ' '))
        stream[j++] = stream[i];
  stream[j] = '\0';

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* splitPosLine(char *line, int pos, char character) {

  if (pos < 0) return NULL;

  int   count = 1, length = 0;
  char *tmpResult = new char[strlen(line) + 1];

  for (int i = 0; i < strlen(line); i++) {
    if (line[i] == character) { count++; i++; }
    if (pos == count) tmpResult[length++] = line[i];
  }
  tmpResult[length] = '\0';

  char *result = new char[length + 1];
  strcpy(result, tmpResult);
  result[length] = '\0';
  if (tmpResult) delete[] tmpResult;

  return result;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int main(int argc, char *argv[]) {

  String* nameCluFile = NULL;
  String* nameSeqFile = NULL;
  String* nameOutFile = NULL;

  if (argc < 4) {
    cout << "Error in the input arguments. The execution must be: " << endl;
    cout << "./ExtractDBClustered <Input cluster file> <Input SMI file> <Output file - GZ>" << endl;
    return 0;
  }
  else {
    nameCluFile = new String(argv[1]);
    nameSeqFile = new String(argv[2]);
    nameOutFile = new String(argv[3]);
  }

  if (!nameCluFile->Length() || !nameSeqFile->Length() || !nameOutFile->Length()) {
    if (nameCluFile)   delete[] nameCluFile;
    if (nameSeqFile)   delete[] nameSeqFile;
    if (nameOutFile)   delete[] nameOutFile;
    return 0;
  }

  TListE <SMILESRef>* listSmiles  = new TListE <SMILESRef> ();
  char                buffer[LENGTHARRAY + 1];
  int                 count       = 0;

  gzFile sequenceFile = gzopen(nameSeqFile->Out(), "rb9");
  while (!gzeof(sequenceFile)) {
    gzgets(sequenceFile, buffer, LENGTHARRAY);
    SMILESRef *lingoFreqEle = new SMILESRef (splitPosLine(buffer, 1, ' '), splitPosLine(buffer, 2, ' '));
    lingoFreqEle->smiles->Trim(); lingoFreqEle->ref->Trim();
    listSmiles->Add(lingoFreqEle);
  }
  gzclose(sequenceFile);

  gzFile clusterFile = gzopen(nameCluFile->Out(),   "rb9");
  gzFile outFile     = gzopen(nameOutFile->Out(),   "wb9");

  while (!gzeof(clusterFile)) {

    gzgets(clusterFile, buffer, LENGTHARRAY);
    
    char *tmpRef = splitPosLine(buffer, 1, ' '); trim(tmpRef);
    SMILESRef *tmpSmilesRef = listSmiles->FirstOcurrence(LingoSpace::FindReference, tmpRef);
    if (tmpRef) delete tmpRef;
    
    if (!tmpSmilesRef) continue;
    gzprintf(outFile, "%s %s\n\0", tmpSmilesRef->smiles->Out(), tmpSmilesRef->ref->Out());
    printf("%s %s\n\0", tmpSmilesRef->smiles->Out(), tmpSmilesRef->ref->Out());

  }

  gzclose(outFile);
  gzclose(clusterFile);

  if (listSmiles)    delete listSmiles;
  
  if (nameCluFile)   delete nameCluFile;
  if (nameSeqFile)   delete nameSeqFile;
  if (nameOutFile)   delete nameOutFile;

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//


